// @ts-check
import * as THREE from 'three'
import _ from 'lodash'

class Engine {
  constructor({
    canvas,
    width,
    height
  }) {
    const scene = new THREE.Scene()
    const camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000)
    this.renderer = new THREE.WebGLRenderer({
      canvas
    })

    const axesHelper = new THREE.AxesHelper(50)
    scene.add(axesHelper)

    this.scene = scene
    scene.fog = new THREE.Fog(0x000000, 0, 250)
    this.camera = camera
    this.updateSize({ width, height })

    this._moveSpeed = 100

    this.cameraTarget = new THREE.Vector3(0, 0, 0)

    camera.rotation.set(0, 0, 0);

    var pitchObject = new THREE.Object3D();
    pitchObject.add(camera);

    var yawObject = new THREE.Object3D();
    yawObject.position.y = 10;
    yawObject.add(pitchObject)
    this.yawObject = yawObject
    this.pitchObject = pitchObject
    this.scene.add(yawObject)
    var light = new THREE.HemisphereLight(0xffffbb, 0x080820, 1)
    scene.add(light)

    this.addLineFloor()
    this.addFloor()
    this.addPlayer()
  }

  addLine = (start, end) => {
    var geometry = new THREE.Geometry()
    var material = new THREE.LineBasicMaterial({ color: 'green' })
    geometry.vertices.push(new THREE.Vector3(start.x, start.y, start.z))
    geometry.vertices.push(new THREE.Vector3(end.x, end.y, end.z))
    var line = new THREE.Line(geometry, material)
    this.scene.add(line)
  }

  addLineFloor = () => {
    const step = 10
    const size = 1000
    const offset = 500

    _.times(size / step, i => {
      const z = i * step - offset
      this.addLine({ x: -500, y: 0, z }, { x: 500, y: 0, z })
    })

    _.times(size / step, i => {
      const x = i * step - offset
      this.addLine({ x, y: 0, z: -500 }, { x, y: 0, z: 500 })
    })
  }

  addMassObjects = () => {
    const step = 10
    const size = 1000
    const offset = 500

    _.times(size / step, i => {
      _.times(size / step, j => {
        const x = i * step - offset
        const y = j * step - offset
        const geometry = new THREE.SphereGeometry(1, 1)
        const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 })
        const cube = new THREE.Mesh(geometry, material)
        cube.position.x = x
        cube.position.z = y
        // cube.position.y = Math.sin(x / 20) * 10
        this.scene.add(cube)
      })
    })
  }

  addPlayer = () => {
    const geometry = new THREE.CubeGeometry(1, 1, 1)
    const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 })
    const cube = new THREE.Mesh(geometry, material)
    // cube.position.x = x
    // cube.position.z = y
    // cube.position.y = Math.sin(x / 20) * 10
    this.scene.add(cube)
  }

  addFloor = () => {
    var geometry = new THREE.PlaneGeometry(1000, 1000, 1, 1)
    var material = new THREE.MeshBasicMaterial({ color: 'white', side: THREE.DoubleSide })
    var plane = new THREE.Mesh(geometry, material)
    plane.position.y = -1
    plane.rotateX(Math.PI / 2)
    this.scene.add(plane)
  }

  updateSize = ({ width, height }) => {
    this.renderer.setSize(width, height)
  }

  rotate = (event) => {
    var PI_2 = Math.PI / 2;

    var movementX = event.movementX || event.mozMovementX || event.webkitMovementX || 0;
    var movementY = event.movementY || event.mozMovementY || event.webkitMovementY || 0;

    this.yawObject.rotation.y -= movementX * 0.002;
    this.pitchObject.rotation.x -= movementY * 0.002;

    this.pitchObject.rotation.x = Math.max(- PI_2, Math.min(PI_2, this.pitchObject.rotation.x));
  }

  move = (direction) => {
    switch (direction) {
      case 'up':
        this.camera.position.z -= this._moveSpeed
        break
      case 'down':
        this.camera.position.z += this._moveSpeed
        break
      case 'left':
        this.camera.position.x -= this._moveSpeed
        break
      case 'right':
        this.camera.position.x += this._moveSpeed
        break
      default:
        break
    }
  }

  next = () => {

  }

  start = () => {
    const animate = () => {
      requestAnimationFrame(animate)
      this.next()
      this.renderer.render(this.scene, this.camera)
    }

    animate()
  }
}

export default Engine
